Skip to content

feat: hmac authentication strategy and response verification#8262

Open
bitgoAaron wants to merge 1 commit intomasterfrom
aloe/hmacWebCryptoStrategy
Open

feat: hmac authentication strategy and response verification#8262
bitgoAaron wants to merge 1 commit intomasterfrom
aloe/hmacWebCryptoStrategy

Conversation

@bitgoAaron
Copy link
Contributor

  • Updated function to be asynchronous, allowing for better handling of HMAC verification.
  • Introduced for default HMAC handling and added support for custom strategies.
  • Integrated for browser compatibility, enabling HMAC signing and verification using the Web Crypto API.
  • Enhanced to utilize the new HMAC strategies for request signing and response verification.
  • Added unit tests for the new HMAC strategies and their integration with the BitGoAPI.
  • Updated web demo to include a new component for WebCrypto authentication.

Ticket: CE-10122

@bitgoAaron bitgoAaron force-pushed the aloe/hmacWebCryptoStrategy branch 2 times, most recently from 729a786 to 0cbe167 Compare March 10, 2026 00:57
@bitgoAaron bitgoAaron marked this pull request as ready for review March 10, 2026 16:01
@bitgoAaron bitgoAaron requested review from a team as code owners March 10, 2026 16:01
- Updated  function to be asynchronous, allowing for better handling
  of HMAC verification.
- Introduced  for default HMAC handling and added support
  for custom strategies.
- Integrated  for browser compatibility, enabling HMAC signing
  and verification using the Web Crypto API.
- Enhanced  to utilize the new HMAC strategies for request signing and response
  verification.
- Added unit tests for the new HMAC strategies and their integration with the BitGoAPI.
- Updated web demo to include a new component for WebCrypto authentication.

Ticket: CE-10122
appendLog('No stored CryptoSigning found in IndexedDB.');
}

const options: Record<string, unknown> = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const options: Record<string, unknown> = {
const options: BitGoAPIOptions = {

I know this is a demo app, but might be worth using a type safe param here so if there's a breaking change, we'd get type errors as another signal something broke

params.timestamp >= now - backwardValidityWindow && params.timestamp <= now + forwardValidityWindow;

return {
isValid: expectedHmac === params.hmac,
Copy link

@mattreid1 mattreid1 Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In hmacv4.ts we use crypto.timingSafeEqual. While this is unlikely to be an issue, probably best to use that here too for timing attack prevention. Might need a polyfill for this though.

const queryPath = extractQueryPath(params.urlPath);

let prefixedText: string;
if (params.statusCode !== undefined && isFinite(params.statusCode) && Number.isInteger(params.statusCode)) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linting suggests Number.isFinite instead of isFinite

VerifyResponseInfo,
} from './types';

function arrayBufToHex(buffer: ArrayBuffer): string {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unbounded, might it be worth putting a max size on this?

Also in Node.js you could do (about 100x faster):

function arrayBufToHexNode(buffer: ArrayBuffer): string {
  return Buffer.from(buffer).toString('hex');
}

Maybe could have different implementations/code paths for browser and Node.js?


I ran a quick benchmark with new ArrayBuffer(1024) // 1KB:

arrayBufToHex: 0.155ms
arrayBufToHexNode: 0.02ms

and new ArrayBuffer(1024 * 1024 * 10) // 10MB:

arrayBufToHex: 487.858ms
arrayBufToHexNode: 4.55ms

const expectedHmac = await webCryptoHmacSign(this.cryptoKey, subject);

const now = Date.now();
const backwardValidityWindow = 1000 * 60 * 5;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for 5 minutes instead of just 1 minute? Not sure how long our timeouts are, but iirc HMAC is done right at the end, just before the request is sent down the wire, so we should only really need to take network latency and user clock drift into account plus some buffer

const bytes = new Uint8Array(buffer);
const hexParts: string[] = new Array(bytes.length);
for (let i = 0; i < bytes.length; i++) {
hexParts[i] = bytes[i].toString(16).padStart(2, '0');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lookup table would be quicker for buffers greater than a few hundred bytes

const hexTable = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));

for (let i = 0; i < bytes.length; i++) {
  hexChars.push(hexTable[bytes[i]]);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants